﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AutoCorrelatorGUI.Model;
using AutoCorrelatorGUI.Properties;
using AutoCorrelatorGUI.Utility;
using AutoCorrelatorGUI.ViewModel.Messages;
using Common.Utility;
using Common.ViewModel;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using GalaSoft.MvvmLight.Messaging;
using OxyPlot;
using OxyPlot.Series;

namespace AutoCorrelatorGUI.ViewModel
{
   /// <summary>
   /// This class contains properties that a View can data bind to.
   /// <para>
   /// See http://www.galasoft.ch/mvvm
   /// </para>
   /// </summary>
   public class PlotDisplayViewModel : ViewModelBase
   {
      private Dictionary<string, LineSeries> _TitleToSeries = new Dictionary<string, LineSeries>();

      /// <summary>
      /// Initializes a new instance of the MvvmViewModel1 class.
      /// </summary>
      public PlotDisplayViewModel()
      {
         Messenger.Default.Register<FullRefreshMessage>(this, OnFullRefresh);
         Messenger.Default.Register<DataUpdateMessage>(this, OnDataUpdate);

         PlotViewModel = new PlotModel() { Title = "Auto Correlation" };

         var xAxis = new OxyPlot.Axes.LinearAxis()
         {
            Position = OxyPlot.Axes.AxisPosition.Bottom,
            Key = "xAxis",
            MajorGridlineStyle = LineStyle.Solid,
            MinorGridlineStyle = LineStyle.None,
            IntervalLength = 50,
            MinorStep = 100,
            Title = "Interval Lag"
         };

         var yAxis = new OxyPlot.Axes.LinearAxis()
         {
            Position = OxyPlot.Axes.AxisPosition.Left,
            Key = "yAxis",
            MajorGridlineStyle = LineStyle.Solid,
            MinorGridlineStyle = LineStyle.None,
            IntervalLength = 50,
            MinorStep = 100,
            Title = "Autocorrelation Coefficient"
         };

         PlotViewModel.Axes.Add(xAxis);
         PlotViewModel.Axes.Add(yAxis);

         PlotViewModel.IsLegendVisible = true;

         PlotViewModel.InvalidatePlot(true);

         //Request any existing data
         Messenger.Default.Send(new RequestDataMessage(OnFullRefresh));
      }

      private MultiSelectBindingList<WellVM> _InternalWellData = null;

      public MultiSelectBindingList<WellVM> InternalWellData
      {
         get { return _InternalWellData; }
         set
         {
            //Need to move the event handlers
            if (_InternalWellData != null)
            {
               _InternalWellData.ListChanged -= OnItemChanged;
            }

            _InternalWellData = value;

            if (_InternalWellData != null)
            {
               _InternalWellData.ListChanged += OnItemChanged;
            }

            RaisePropertyChanged("InternalWellData");
         }
      }

      private void OnFullRefresh(FullRefreshMessage message)
      {
         InternalWellData = message.DataView.SourceCollection as MultiSelectBindingList<WellVM>;

         CreateAllSeries();
         SyncAllProps();
         SyncAllData();
      }

      private void OnDataUpdate(DataUpdateMessage message)
      {
         if (message.UpdateType == DataUpdateTypes.Output)
         {
            SyncAllData();

            foreach (var axis in PlotViewModel.Axes)
            {
               axis.Reset();
            }
         }
      }

      private void OnItemChanged(object sender, ListChangedEventArgs e)
      {
         SyncAllProps();
      }

      private void CreateAllSeries()
      {
         //Clear out all of the series and create new ones for each Well
         PlotViewModel.Series.Clear();
         _TitleToSeries.Clear();

         for (int i = 0; i < InternalWellData.Count; i++)
         {
            var series = new OxyPlot.Series.LineSeries() { MarkerType = DefaultPalettes.Marker[i % DefaultPalettes.Marker.Length] };
            _TitleToSeries[InternalWellData[i].Title] = series;

            PlotViewModel.Series.Add(series);
         }
      }

      private void SyncAllProps()
      {
         for (int i = 0; i < InternalWellData.Count; i++)
         {
            SyncWellPropsToSeries(InternalWellData[i], _TitleToSeries[InternalWellData[i].Title]);
         }

         PlotViewModel.InvalidatePlot(true);
      }

      private void SyncWellPropsToSeries(WellVM well, LineSeries series)
      {
         series.Title = well.Title;
         series.IsVisible = well.Visible;

         OxyColor color = OxyColor.FromArgb(well.Color.A, well.Color.R, well.Color.G, well.Color.B);

         switch (well.Focus)
         {
         case FocusLevels.Low:
            SeriesSetColorSize(series, OxyColor.FromAColor((byte)(well.Color.A / 10), color), 2.0);
            break;
         case FocusLevels.Normal:
            SeriesSetColorSize(series, color, 2.0);
            break;
         case FocusLevels.High:
            SeriesSetColorSize(series, color, 4.0);
            break;
         default:
            break;
         }
      }

      void SeriesSetColorSize(LineSeries series, OxyColor color, double size)
      {
         series.Color = color;
         series.MarkerFill = color;
         series.MarkerStroke = color;
         series.StrokeThickness = size;
         series.MarkerSize = 1.0 + size;
         series.MarkerStrokeThickness = 1.0;
      }

      private void SyncAllData()
      {
         for (int i = 0; i < InternalWellData.Count; i++)
         {
            SyncWellDataToSeries(InternalWellData[i] as WellVM, _TitleToSeries[InternalWellData[i].Title]);
         }

         PlotViewModel.InvalidatePlot(true);
      }

      private void SyncWellDataToSeries(WellVM well, LineSeries series)
      {
         series.Points.Clear();
         series.Points.AddRange(well.Output.Data);
      }

      public PlotModel PlotViewModel
      {
         get;
         private set;
      }
   }
}
